package yscmd

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gcmd"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
)

func SetFlag(cmd ICommandBuilder, falg IFlagBuilder, group ...string) bool {
	falg.Init(cmd.Command().Context(), group...)
	return cmd.Command().AddFlag(falg.Flag())
}

func NewDefaultFlag(group ...string) *Flag {
	f := &Flag{
		ConfigNodeName: "app",
	}

	if !g.IsEmpty(group) {
		f.Group = group[0]
	}

	return f
}

func SetDefaultFlag(cmd ICommandBuilder) {
	cmd.SetFlag(&FlagDebugBuilder{})
	cmd.SetFlag(&FlagCfgFileBuilder{})
	cmd.SetFlag(&FlagCfgPathBuilder{})
	cmd.SetFlag(&FlagVerboseBuilder{})
}

type IFlagBuilder interface {
	// Init 初始化命令
	Init(context.Context, ...string)

	// Flag 获取此
	Flag() *Flag
}

type FlagBuilderCore struct {
	F *Flag
}

func (f *FlagBuilderCore) Init(ctx context.Context, group ...string) {
	f.F = NewDefaultFlag(group...)
	f.F.ctx = ctx
}

func (f *FlagBuilderCore) Flag() *Flag {
	return f.F
}

type Flag struct {
	Short          string          // 选项 短
	Long           string          // 选择 长
	DefaultValue   interface{}     // 带参数时的参数名
	Description    string          // 描述信息
	ConfigNodeName string          // 配置文件节点名
	Group          string          // 配置节点中的分组, 更多的区分同一种配置参数, 还是尽量配置参数不一样
	ctx            context.Context // 用于存储链式传递的 ctx
}

func (f *Flag) SetContext(ctx context.Context) {
	f.ctx = ctx
}

func (f *Flag) Context() context.Context {
	return f.ctx
}

func (f *Flag) getFlagInfo() string {
	short := ""
	if !g.IsEmpty(f.ShortKey()) {
		short += "-" + f.ShortKey()
	}
	long := ""
	if !g.IsEmpty(f.LongKey()) {
		long += "--" + f.LongKey()
	}

	flag := ""
	flag += short
	if !g.IsEmpty(short) && !g.IsEmpty(long) {
		flag += ","
	}
	flag += long

	// 显示信息
	// <选项标记> <默认值> <配置文件节点> <描述信息>
	info := "    "
	info += flag + " "
	if !g.IsNil(f.DefaultValue) {
		info += fmt.Sprintf("[默认值: %s] ", gconv.String(f.DefaultValue))
	}

	if !g.IsEmpty(f.ConfigKey()) {
		info += fmt.Sprintf("[节点: %s] ", f.ConfigKey())
	}
	info += f.Description

	return info
}

// GetVar 获取标志参数值。
// 按照 全配置名称 -> 长参数 -> 短参数 -> [长参数不空]配置文件(驼峰变量->蛇形变量->long变量) -> 环境变量 的顺序读取
func (f *Flag) GetVar() (o *g.Var) {

	o = f.GetVarInOpt()
	if !g.IsNil(o) {
		return o
	}

	o = f.GetVarInCfg()
	if !g.IsNil(o) {
		return o
	}

	o = f.GetVarInEnv()
	if !g.IsNil(o) {
		return o
	}

	return f.GetVarDefault()
}

// GetVarWithOpt 通过命令行参数获取值，
// 当值不存在时，使用默认值
func (f *Flag) GetVarWithOpt() (o *g.Var) {
	o = f.GetVarInOpt()
	if !g.IsNil(o) {
		return o
	}

	return f.GetVarDefault()
}

// GetVarWithCfg 通过配置文件获取值，
// 当值不存在时，使用默认值
func (f *Flag) GetVarWithCfg() (o *g.Var) {
	o = f.GetVarInCfg()
	if !g.IsNil(o) {
		return o
	}

	return f.GetVarDefault()
}

// GetVarWithEnv 通过环境变量获取值，
// 当值不存在时，使用默认值
func (f *Flag) GetVarWithEnv() (o *g.Var) {
	o = f.GetVarInEnv()
	if !g.IsNil(o) {
		return o
	}

	return f.GetVarDefault()
}

// GetVarInOpt 只通过命令行参数获取值
// 全配置名称 -> 长参数 -> 短参数
// 不存在时返回 nil
func (f *Flag) GetVarInOpt() (o *g.Var) {
	if gcmd.GetOpt(f.ConfigKey()) != nil {
		return gcmd.GetOpt(f.ConfigKey())
	}

	if gcmd.GetOpt(f.LongKey()) != nil {
		return gcmd.GetOpt(f.LongKey())
	}

	if gcmd.GetOpt(f.ShortKey()) != nil {
		return gcmd.GetOpt(f.ShortKey())
	}

	return nil
}

// GetVarInCfg 只通过配置文件获取值
// 不存在时返回 nil
func (f *Flag) GetVarInCfg() *g.Var {
	if f.HasCfg() {
		value := g.Cfg().MustGet(f.ctx, f.ConfigKeyCamelLower())

		if value.IsEmpty() {
			value = g.Cfg().MustGet(f.ctx, f.ConfigKeySnake())
		}

		if value.IsEmpty() {
			value = g.Cfg().MustGet(f.ctx, f.ConfigKey())
		}

		return value
	}

	return nil
}

// GetVarInEnv 只通过环境变量获取值
// 不存在时返回 nil
func (f *Flag) GetVarInEnv() *g.Var {
	if f.HasEnv() {
		env := gcmd.GetOptWithEnv(f.ConfigKey())
		return env
	}

	return nil
}

// GetVarDefault 获取默认值的 Var
func (f *Flag) GetVarDefault() *g.Var {
	return g.NewVar(f.DefaultValue)
}

// Get 获取字符串结果，建议不使用
func (f *Flag) Get() string {
	return f.GetVar().String()
}

func (f *Flag) GetWithCfg() string {
	return f.GetVarInCfg().String()
}

// 计算是否存在 group 前缀
func (f *Flag) groupStr() string {
	group := ""
	if !g.IsEmpty(f.Group) {
		group = f.Group + "."
	}
	return group
}

func (f *Flag) LongKey() string {
	if g.IsEmpty(f.Long) {
		return ""
	}
	return f.groupStr() + f.Long
}

func (f *Flag) ShortKey() string {
	if g.IsEmpty(f.Short) {
		return ""
	}
	return f.groupStr() + f.Short
}

func (f *Flag) configKey(key string) string {
	if g.IsEmpty(f.ConfigNodeName) || g.IsEmpty(key) {
		return ""
	}

	return fmt.Sprintf(`%s.%s%s`, f.ConfigNodeName, f.groupStr(), key)
}

// ConfigKey 获取当前 Config 的配置项名称, 此名称应该在软件内唯一
// 此参数也用于 Command.AddFlag 中存储 Flag 使用
func (f *Flag) ConfigKey() string {
	return f.configKey(f.Long)
}

// ConfigKeyCamelLower 驼峰式的 configKey
func (f *Flag) ConfigKeyCamelLower() string {
	return f.configKey(gstr.CaseCamelLower(f.Long))
}

// ConfigKeySnake 蛇式的 config_key
func (f *Flag) ConfigKeySnake() string {
	return f.configKey(gstr.CaseSnake(f.Long))
}

// Has 按照 命令行,配置文件,环境变量的顺序读取
func (f *Flag) Has() bool {

	if f.HasCmd() {
		return true
	}

	if f.HasCfg() {
		return true
	}

	if f.HasEnv() {
		return true
	}

	return false
}

// HasCmd 命令行是否存在
func (f *Flag) HasCmd() bool {
	if gcmd.GetOpt(f.ShortKey()) != nil || gcmd.GetOpt(f.LongKey()) != nil {
		return true
	}
	return false
}

// HasCfg 配置文件是否存在
func (f *Flag) HasCfg() bool {
	if g.Config().Available(f.ctx) {
		if f.checkCfgKey(f.ConfigKeyCamelLower()) {
			return true
		}
		if f.checkCfgKey(f.ConfigKeySnake()) {
			return true
		}
		if f.checkCfgKey(f.ConfigKey()) {
			return true
		}
	}
	return false
}

func (f *Flag) checkCfgKey(key string) bool {
	if v, _ := g.Cfg().Get(f.ctx, key); v != nil {
		return true
	}
	return false
}

// HasEnv 环境变量是否存在
func (f *Flag) HasEnv() bool {
	if !g.IsEmpty(f.ConfigKey()) {
		env := gcmd.GetOptWithEnv(f.ConfigKey())
		return !env.IsNil()
	}
	return false
}

func (f *Flag) DefaultCfg() g.MapStrAny {
	return g.MapStrAny{
		f.ConfigKeyCamelLower(): f.DefaultValue,
	}
}
